package io.youngledo.usb.storage.impl;

import com.baidubce.BceClientConfiguration;
import com.baidubce.auth.DefaultBceCredentials;
import com.baidubce.services.bos.BosClient;
import com.baidubce.services.bos.BosClientConfiguration;
import com.baidubce.services.bos.model.*;
import io.youngledo.usb.storage.*;
import io.youngledo.usb.exception.StorageConnectionException;
import io.youngledo.usb.exception.StorageOperationException;
import io.youngledo.usb.config.TimeoutConfig;
import io.youngledo.usb.task.ProgressTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/// 百度云BOS存储服务适配器，[地域和访问域名](https://cloud.baidu.com/doc/BOS/s/akrqd2wcx)
///
public class BaiduStorageService extends AbstractObjectStorageService {

    private static final Logger logger = LoggerFactory.getLogger(BaiduStorageService.class);

    private BosClient bosClient;
    private BosClientConfiguration bosConfig;

    @Override
    public void connect(StorageConfig config) {
        if (config.type() != StorageType.BAIDU_BOS) {
            throw new IllegalArgumentException("Invalid storage type for Baidu BOS service: " + config.type());
        }

        this.config = config;

        bosConfig = new BosClientConfiguration();
        bosConfig.setCredentials(new DefaultBceCredentials(config.accessKey(), config.secretKey()));
        bosConfig.setEndpoint(config.endpoint());

        // 设置统一的超时配置
        bosConfig.setConnectionTimeoutInMillis(TimeoutConfig.CONNECTION_TIMEOUT_MS);
        bosConfig.setSocketTimeoutInMillis(TimeoutConfig.SOCKET_TIMEOUT_MS);

        this.bosClient = new BosClient(bosConfig);
        // 测试连接 - 尝试列出bucket来验证连接
        bosClient.listBuckets();
        this.connected = true;
    }

    @Override
    protected void performDisconnect() {
        if (bosClient != null) {
            try {
                bosClient.shutdown();
                logger.info("Disconnected from Baidu BOS");
            } catch (Exception e) {
                logger.warn("Error during BOS client shutdown: {}", e.getMessage());
            }
            bosClient = null;
        }
        bosConfig = null;
    }

    @Override
    public List<StorageBucket> listBuckets() {
        checkConnection();
        return bosClient.listBuckets().getBuckets()
                .parallelStream()
                .map(bucket -> {
                    LocalDateTime creationTime = bucket.getCreationDate()
                            .toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDateTime();
                    return StorageBucket.of(bucket.getName(), creationTime);
                }).toList();
    }

    @Override
    public void createBucket(String bucketName) {
        checkConnection();
        try {
            CreateBucketRequest request = new CreateBucketRequest(bucketName);
            bosClient.createBucket(request);
            logger.info("Successfully created bucket: {}", bucketName);
        } catch (Exception e) {
            logger.error("Failed to create bucket {}: {}", bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to create bucket: " + e.getMessage(), e);
        }
    }

    @Override
    public void deleteBucket(String bucketName) {
        checkConnection();
        try {
            bosClient.deleteBucket(bucketName);
            logger.info("Successfully deleted bucket: {}", bucketName);
        } catch (Exception e) {
            logger.error("Failed to delete bucket {}: {}", bucketName, e.getMessage(), e);
            throw StorageOperationException.bucketNotFound(bucketName);
        }
    }

    @Override
    public List<StorageObject> listObjects(String bucketName, String prefix, boolean recursive) {
        checkConnection();
        ListObjectsRequest request = new ListObjectsRequest(bucketName);
        if (prefix != null && !prefix.isEmpty()) {
            request.setPrefix(prefix);
        }
        if (!recursive) {
            request.setDelimiter("/");
        }

        ListObjectsResponse response = bosClient.listObjects(request);
        List<StorageObject> objects = new ArrayList<>();

        // 处理文件对象
        objects.addAll(ObjectStorageHelper.processObjects(
                response.getContents(),
                prefix,
                BosObjectSummary::getKey,
                (obj, key, displayName) -> {
                    LocalDateTime lastModified = obj.getLastModified()
                            .toInstant()
                            .atZone(ZoneId.systemDefault())
                            .toLocalDateTime();

                    return new StorageObject(
                            key,
                            displayName,
                            obj.getSize(),
                            lastModified,
                            false,
                            obj.getETag()
                    );
                }
        ));

        // 处理目录（如果不是递归模式）
        if (!recursive && response.getCommonPrefixes() != null) {
            objects.addAll(ObjectStorageHelper.processCommonPrefixes(
                    response.getCommonPrefixes(),
                    prefix,
                    false  // 不移除末尾的斜杠
            ));
        }
        return objects;
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, File file) {
        checkConnection();
        try {
            PutObjectRequest request = new PutObjectRequest(bucketName, objectKey, file);
            bosClient.putObject(request);
            logger.info("Successfully uploaded file {} to bucket {}", objectKey, bucketName);
        } catch (Exception e) {
            logger.error("Failed to upload file {} to bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, InputStream inputStream, long contentLength) {
        checkConnection();
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(contentLength);

            PutObjectRequest request = new PutObjectRequest(bucketName, objectKey, inputStream, metadata);
            bosClient.putObject(request);

            logger.info("Successfully uploaded stream {} to bucket {}", objectKey, bucketName);
        } catch (Exception e) {
            logger.error("Failed to upload stream {} to bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @Override
    public InputStream downloadFile(String bucketName, String objectKey) {
        checkConnection();
        try {
            GetObjectRequest request = new GetObjectRequest(bucketName, objectKey);
            BosObject object = bosClient.getObject(request);
            return object.getObjectContent();
        } catch (Exception e) {
            logger.error("Failed to download object {} from bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void downloadFile(String bucketName, String objectKey, File localFile) {
        checkConnection();
        try (InputStream inputStream = downloadFile(bucketName, objectKey);
             FileOutputStream outputStream = new FileOutputStream(localFile)) {
            inputStream.transferTo(outputStream);
        } catch (IOException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void deleteObject(String bucketName, String objectKey) {
        checkConnection();
        try {
            bosClient.deleteObject(bucketName, objectKey);
            logger.info("Successfully deleted object {} from bucket {}", objectKey, bucketName);
        } catch (Exception e) {
            logger.error("Failed to delete object {} from bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void deleteObjects(String bucketName, List<String> objectKeys) {
        checkConnection();
        try {
            if (objectKeys.isEmpty()) {
                return;
            }

            DeleteMultipleObjectsRequest request = new DeleteMultipleObjectsRequest();
            request.setBucketName(bucketName);
            request.setObjectKeys(objectKeys);

            bosClient.deleteMultipleObjects(request);
            logger.info("Successfully deleted {} objects from bucket {}", objectKeys.size(), bucketName);
        } catch (Exception e) {
            logger.error("Failed to delete objects from bucket {}: {}", bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to delete objects: " + e.getMessage(), e);
        }
    }

    @Override
    public StorageObject getObjectInfo(String bucketName, String objectKey) {
        checkConnection();
        try {
            GetObjectMetadataRequest request = new GetObjectMetadataRequest(bucketName, objectKey);
            ObjectMetadata metadata = bosClient.getObjectMetadata(request);

            String displayName = objectKey;
            if (displayName.contains("/")) {
                displayName = displayName.substring(displayName.lastIndexOf('/') + 1);
            }

            LocalDateTime lastModified = metadata.getLastModified()
                    .toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDateTime();

            return new StorageObject(
                    objectKey,
                    displayName,
                    metadata.getContentLength(),
                    lastModified,
                    false,
                    metadata.getETag()
            );
        } catch (Exception e) {
            logger.error("Failed to get object info for {} in bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public StorageType getStorageType() {
        return StorageType.BAIDU_BOS;
    }

    @Override
    public String generatePresignedUrl(String bucketName, String objectKey, int expiry) {
        checkConnection();
        try {
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
                    bucketName,
                    objectKey,
                    com.baidubce.http.HttpMethodName.GET
            );
            request.setExpiration((int) (System.currentTimeMillis() / 1000 + expiry));

            URL url = bosClient.generatePresignedUrl(request);
            return url.toString();
        } catch (Exception e) {
            logger.error("Failed to generate presigned URL for {} in bucket {}: {}", objectKey, bucketName, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to generate presigned URL: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFileWithProgress(String bucketName, String objectKey, File file, ProgressTracker progressTracker) {
        checkConnection();
        try {
            progressTracker.statusProperty().set("开始上传到百度云BOS...");

            // 由于百度云SDK没有内置进度回调，我们使用简单的上传方式
            // 在实际生产环境中，可以考虑分块上传来实现更精确的进度追踪
            uploadFile(bucketName, objectKey, file);

            progressTracker.complete();
        } catch (Exception e) {
            logger.error("Failed to upload file {} with progress: {}", objectKey, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file with progress: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFileWithProgress(String bucketName, String objectKey, InputStream inputStream, long contentLength, ProgressTracker progressTracker) {
        checkConnection();
        try {
            progressTracker.statusProperty().set("开始上传到百度云BOS...");

            uploadFile(bucketName, objectKey, inputStream, contentLength);

            progressTracker.complete();
        } catch (Exception e) {
            logger.error("Failed to upload stream {} with progress: {}", objectKey, e.getMessage(), e);
            throw StorageOperationException.operationFailed("Failed to upload file stream with progress: " + e.getMessage(), e);
        }
    }

}